﻿//  Copyright 2015 Google Inc. All Rights Reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.

using NtCoreLib;
using NtCoreLib.Security.Authorization;
using NtCoreLib.Security.Token;
using NtCoreLib.Win32.Process;
using NtCoreLib.Win32.Security;
using NtCoreLib.Win32.Security.Authentication;
using NtCoreLib.Win32.Security.Authorization.AclUI;
using NtCoreLib.Win32.Security.Safer;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace TokenViewer;

public partial class TokenForm : Form
{
    private readonly NtToken _token;

    private static void PopulateGroupList(ListView listView, IEnumerable<UserGroup> groups, bool filter_il)
    {
        foreach (UserGroup group in groups)
        {
            GroupAttributes flags = group.Attributes & ~(GroupAttributes.EnabledByDefault);

            if (filter_il && flags.HasFlag(GroupAttributes.Integrity))
            {
                continue;
            }

            ListViewItem item = new(group.ToString());
            item.SubItems.Add(flags.ToString());

            if (flags.HasFlag(GroupAttributes.Enabled))
            {
                item.BackColor = Color.LightGreen;
            }
            else if (flags.HasFlag(GroupAttributes.UseForDenyOnly))
            {
                item.BackColor = Color.LightSalmon;
            }
            item.Tag = group;
            listView.Items.Add(item);
        }
        listView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        listView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
    }

    private void UpdateGroupList()
    {
        listViewGroups.Items.Clear();
        List<UserGroup> groups = new();
        groups.Add(_token.User);
        groups.AddRange(_token.Groups);

        PopulateGroupList(listViewGroups, groups, true);
    }

    private void UpdatePrivileges()
    {
        listViewPrivs.Items.Clear();
        foreach (TokenPrivilege priv in _token.Privileges)
        {
            ListViewItem item = new(priv.Name);
            bool enabled = false;
            string flags = "Disabled";

            if (priv.Attributes.HasFlag(PrivilegeAttributes.Enabled))
            {
                enabled = true;
                flags = "Enabled";
            }

            if (priv.Attributes.HasFlag(PrivilegeAttributes.EnabledByDefault))
            {
                flags = "Default " + flags;
            }

            item.SubItems.Add(flags);
            item.SubItems.Add(priv.DisplayName);

            item.BackColor = enabled ? Color.LightGreen : Color.LightPink;
            item.Tag = priv;

            listViewPrivs.Items.Add(item);
        }
        listViewPrivs.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        listViewPrivs.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
    }

    private static string FormatAttributeValue(object value)
    {
        if (value is byte[] bytes)
        {
            StringBuilder builder = new();
            builder.AppendFormat("Length {0} - {{", bytes.Length);
            int count = bytes.Length;
            builder.Append(string.Join(", ", bytes.Take(count > 16 ? 16 : count).Select(b => $"0x{b:X02}")));

            if (count > 16)
            {
                builder.Append(", ...");
            }
            builder.Append("}");
            return builder.ToString();
        }
        else if (value is ulong l)
        {
            return $"{l:X016}";
        }

        return value.ToString();
    }

    private void UpdateSecurityAttributes(TabPage tab_page, TreeView treeView, SecurityAttributeType type)
    {
        var attrs = _token.GetSecurityAttributes(type, false);
        if (!attrs.IsSuccess || attrs.Result.Length == 0)
        {
            tabControlSecurityAttributes.TabPages.Remove(tab_page);
        }
        treeView.Nodes.Clear();
        foreach (ClaimSecurityAttribute attr in attrs.Result)
        {
            TreeNode node = new(attr.Name);
            node.Nodes.Add($"Flags: {attr.Flags}");
            node.Nodes.Add($"Type: {attr.ValueType}");
            int value_index = 0;
            foreach (object value in attr.Values)
            {
                node.Nodes.Add($"Value {value_index++}: {FormatAttributeValue(value)}");
            }
            treeView.Nodes.Add(node);
        }
        foreach (TreeNode node in treeView.Nodes)
        {
            node.Expand();
        }
    }

    private void UpdateTokenFlags()
    {
        txtTokenFlags.Text = _token.Flags.ToString();
    }

    private void UpdateTokenData(ProcessTokenEntry? process)
    {
        UserGroup user = _token.User;

        txtUsername.Text = user.ToString();
        txtUserSid.Text = user.Sid.ToString();

        TokenType tokentype = _token.TokenType;

        txtTokenType.Text = tokentype.ToString();

        if (tokentype == TokenType.Impersonation)
        {
            txtImpLevel.Text = _token.ImpersonationLevel.ToString();
        }
        else
        {
            txtImpLevel.Text = "N/A";
        }

        txtTokenId.Text = _token.Id.ToString();
        txtModifiedId.Text = _token.ModifiedId.ToString();
        txtAuthId.Text = _token.AuthenticationId.ToString();
        if (Enum.IsDefined(typeof(TokenIntegrityLevel), _token.IntegrityLevel))
        {
            comboBoxIL.SelectedItem = _token.IntegrityLevel;
            comboBoxILForDup.SelectedItem = _token.IntegrityLevel;
        }
        else
        {
            comboBoxIL.Text = _token.IntegrityLevel.ToString();
            comboBoxILForDup.Text = _token.IntegrityLevel.ToString();
        }

        txtSessionId.Text = _token.SessionId.ToString();
        if (_token.IsAccessGranted(TokenAccessRights.QuerySource))
        {
            txtSourceName.Text = _token.Source.SourceName;
            txtSourceId.Text = _token.Source.SourceIdentifier.ToString();
        }
        else
        {
            txtSourceName.Text = "N/A";
            txtSourceId.Text = "N/A";
        }
        TokenElevationType evtype = _token.ElevationType;
        txtElevationType.Text = evtype.ToString();
        txtIsElevated.Text = _token.Elevated.ToString();
        txtOriginLoginId.Text = _token.Origin.ToString();

        btnLinkedToken.Enabled = evtype != TokenElevationType.Default;
        btnLinkedToken.Visible = btnLinkedToken.Enabled;

        UpdateGroupList();

        txtPrimaryGroup.Text = _token.PrimaryGroup.Name;
        txtOwner.Text = _token.Owner.Name;

        Acl defdacl = _token.DefaultDacl;
        if (!defdacl.NullAcl)
        {
            foreach (Ace ace in defdacl)
            {
                UserGroup group = new(ace.Sid, GroupAttributes.None);

                ListViewItem item = new(group.ToString());

                AccessMask mask = GenericAccessRights.GenericAll | GenericAccessRights.GenericExecute 
                    | GenericAccessRights.GenericRead | GenericAccessRights.GenericWrite 
                    | GenericAccessRights.AccessSystemSecurity | GenericAccessRights.Delete | GenericAccessRights.ReadControl
                    | GenericAccessRights.Synchronize | GenericAccessRights.WriteDac | GenericAccessRights.WriteOwner;
                string maskstr;

                if ((ace.Mask & ~mask).HasAccess)
                {
                    maskstr = $"0x{ace.Mask:X08}";
                }
                else
                {
                    maskstr = ace.Mask.ToGenericAccess().ToString();
                }

                item.SubItems.Add(maskstr);
                item.SubItems.Add(ace.Flags.ToString());
                item.SubItems.Add(ace.Type.ToString());
                listViewDefDacl.Items.Add(item);
            } 
        }
        else
        {
            listViewDefDacl.Items.Add("No Default DACL");
        }

        listViewDefDacl.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        listViewDefDacl.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);

        if (_token.Restricted)
        {
            PopulateGroupList(listViewRestrictedSids, _token.RestrictedSids, false);
            if (_token.WriteRestricted)
            {
                tabPageRestricted.Text = "Write Restricted SIDs";
            }
        }
        else
        {
            tabControlMain.TabPages.Remove(tabPageRestricted);
        }

        if (_token.AppContainer)
        {
            PopulateGroupList(listViewCapabilities, _token.Capabilities, false);
            txtACNumber.Text = _token.AppContainerNumber.ToString();
            txtPackageName.Text = _token.AppContainerSid.Name;
            txtPackageSid.Text = _token.AppContainerSid.ToString();
        }
        else
        {
            tabControlMain.TabPages.Remove(tabPageAppContainer);
        }

        if (process == null)
        {
            tabControlMain.TabPages.Remove(tabPageTokenSource);
        }
        else
        {
            txtProcessId.Text = process.ProcessId.ToString();
            txtProcessImagePath.Text = process.ImagePath;
            txtProcessCommandLine.Text = process.CommandLine;
            if (process is ThreadTokenEntry thread)
            {
                txtThreadId.Text = thread.ThreadId.ToString();
                txtThreadName.Text = thread.ThreadName;
            }
            else
            {
                groupThread.Visible = false;
            }
        }

        txtUIAccess.Text = _token.UIAccess.ToString();
        txtSandboxInert.Text = _token.SandboxInert.ToString();
        bool virtAllowed = _token.VirtualizationAllowed;
        txtVirtualizationAllowed.Text = virtAllowed.ToString();
        btnToggleVirtualizationEnabled.Enabled = virtAllowed;
        btnToggleVirtualizationEnabled.Visible = virtAllowed;
        if (virtAllowed)
        {
            txtVirtualizationEnabled.Text = _token.VirtualizationEnabled.ToString();
        }
        else
        {
            txtVirtualizationEnabled.Text = "N/A";
        }

        txtMandatoryILPolicy.Text = _token.MandatoryPolicy.ToString();
        txtHandleAccess.Text = _token.GrantedAccess.ToString();
        Sid trust_level = _token.TrustLevel;
        txtTrustLevel.Text = trust_level != null ? trust_level.Name : "N/A";
        UpdateTokenFlags();
        UpdatePrivileges();
        UpdateSecurityAttributes(tabPageLocalSecurityAttributes, treeViewLocalSecurityAttributes, SecurityAttributeType.Local);
        UpdateSecurityAttributes(tabPageUserClaimSecurityAttributes, treeViewUserClaimSecurityAttributes, SecurityAttributeType.User);
        UpdateSecurityAttributes(tabPageDeviceClaimSecurityAttributes, treeViewDeviceClaimSecurityAttributes, SecurityAttributeType.Device);
        if (_token.DeviceGroups.Length > 0)
        {
            PopulateGroupList(listViewDeviceGroup, _token.DeviceGroups, false);
        }
        else
        {
            tabControlSecurityAttributes.TabPages.Remove(tabPageDeviceGroup);
        }
        if (tabControlSecurityAttributes.TabCount == 0)
        {
            lblSecurityAttributes.Visible = false;
        }

        if (_token.IsAccessGranted(TokenAccessRights.ReadControl))
        {
            securityDescriptorViewerControl.SetSecurityDescriptor(_token.SecurityDescriptor, _token.NtType, _token.NtType.ValidAccess);
        }
        else
        {
            tabControlMain.TabPages.Remove(tabPageSecurity);
        }
    }

    private static string GetFormText(NtToken token, string? text)
    {
        StringBuilder builder = new();
        if (!string.IsNullOrWhiteSpace(text))
        {
            builder.AppendFormat("{0} - ", text);
        }
        builder.AppendFormat("User {0} - TokenId {1}",
            token.User.Sid.Name, token.Id);
        return builder.ToString();
    }

    public TokenForm(NtToken token) 
        : this(token, null)
    {
    }

    public TokenForm(NtToken token, string? text) 
        : this(null, token, text)
    {
    }

    private TokenForm(ProcessTokenEntry? process, NtToken token, string? text)
    {
        InitializeComponent();
        this.Disposed += TokenForm_Disposed;
        _token = token;
        Text = GetFormText(token, text);

        foreach (object v in Enum.GetValues(typeof(TokenIntegrityLevel)))
        {
            comboBoxIL.Items.Add(v);
            comboBoxILForDup.Items.Add(v);
        }

        UpdateTokenData(process);
        listViewGroups.ListViewItemSorter = new ListItemComparer(0);
        listViewPrivs.ListViewItemSorter = new ListItemComparer(0);
        listViewRestrictedSids.ListViewItemSorter = new ListItemComparer(0);
        listViewCapabilities.ListViewItemSorter = new ListItemComparer(0);

        comboBoxImpLevel.Items.Add(SecurityImpersonationLevel.Anonymous);
        comboBoxImpLevel.Items.Add(SecurityImpersonationLevel.Identification);
        comboBoxImpLevel.Items.Add(SecurityImpersonationLevel.Impersonation);
        comboBoxImpLevel.Items.Add(SecurityImpersonationLevel.Delegation);
        comboBoxImpLevel.SelectedItem = SecurityImpersonationLevel.Impersonation;

        comboBoxTokenType.Items.Add(TokenType.Primary);
        comboBoxTokenType.Items.Add(TokenType.Impersonation);
        comboBoxTokenType.SelectedItem = TokenType.Impersonation;

        foreach (object v in Enum.GetValues(typeof(SaferLevelId)))
        {
            comboBoxSaferLevel.Items.Add(v);
        }

        comboBoxSaferLevel.SelectedItem = SaferLevelId.NormalUser;
    }

    internal TokenForm(ProcessTokenEntry process, string? text, bool thread)
        : this(process, thread ? ((ThreadTokenEntry)process).ThreadToken : process.ProcessToken, text)
    {
    }

    void TokenForm_Disposed(object sender, EventArgs e)
    {
        _token.Dispose();
    }

    private bool ShowTokenPermissions(TokenAccessRights access, bool throw_on_error)
    {
        using var result = _token.Duplicate(access, AttributeFlags.None, DuplicateObjectOptions.None, throw_on_error);
        if (result.IsSuccess)
        {
            using var dlg = new EditSecurityDescriptorDialog(result.Result, "Token", false);
            dlg.Show(Handle);
            return true;
        }
        return false;
    }

    private void btnEditPermissions_Click(object sender, EventArgs e)
    {
        try
        {
            if (!ShowTokenPermissions(TokenAccessRights.ReadControl | TokenAccessRights.WriteDac, false))
            {
                ShowTokenPermissions(TokenAccessRights.ReadControl, true);
            }
        }
        catch (NtException ex)
        {
            MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    static List<TokenForm> _forms = new();
    static IWin32Window? _main_form;

    public static void RegisterMainForm(MainForm window)
    {
        _main_form = window;
    }

    private static void OpenForm(TokenForm form)
    {
        _forms.Add(form);
        form.FormClosed += form_FormClosed;
        form.Show(_main_form);
    }

    public static void OpenForm(NtToken? token, string? text, bool copy)
    {
        if (token != null)
        {
            OpenForm(new TokenForm(copy ? token.Duplicate() : token, text));
        }
    }

    internal static void OpenForm(ProcessTokenEntry process, string? text, bool copy, bool thread)
    {
        if (process != null)
        {
            OpenForm(new TokenForm(copy ? process.Clone() : process, text, thread));
        }
    }

    static void form_FormClosed(object sender, FormClosedEventArgs e)
    {
        if (sender is TokenForm form)
        {
            _forms.Remove(form);
            form.Dispose();
        }
    }

    private void btnLinkedToken_Click(object sender, EventArgs e)
    {
        try
        {
            OpenForm(_token.GetLinkedToken(), "Linked", false);
        }
        catch (Exception ex)
        {
            MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void comboBoxTokenType_SelectedIndexChanged(object sender, EventArgs e)
    {
        if ((TokenType)comboBoxTokenType.SelectedItem != TokenType.Primary)
        {
            comboBoxImpLevel.Enabled = true;
        }
        else
        {
            comboBoxImpLevel.Enabled = false;
        }
    }

    private void btnDuplicate_Click(object sender, EventArgs e)
    {
        try
        {
            using NtToken token = _token.DuplicateToken((TokenType)comboBoxTokenType.SelectedItem,
                    (SecurityImpersonationLevel)comboBoxImpLevel.SelectedItem, TokenAccessRights.MaximumAllowed);
            TokenIntegrityLevel il = GetILFromComboBox(comboBoxILForDup);
            if (il != token.IntegrityLevel)
            {
                token.SetIntegrityLevel(il);
            }
            OpenForm(token, "Duplicate", true);
        }
        catch (Exception ex)
        {
            MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    static NtToken CreateProcessForToken(string cmdline, NtToken token, bool make_interactive)
    {
        using NtToken newtoken = token.DuplicateToken(TokenType.Primary, SecurityImpersonationLevel.Anonymous, TokenAccessRights.MaximumAllowed);
        Win32ProcessConfig config = new()
        {
            Token = newtoken,
            CommandLine = cmdline
        };
        if (make_interactive)
        {
            config.Desktop = @"WinSta0\Default";
            newtoken.SetSessionId(NtProcess.Current.SessionId);
        }

        using var process = config.Create();
        return process.Process.OpenToken();
    }

    private void btnCreateProcess_Click(object sender, EventArgs e)
    {
        try
        {
            if (checkBoxUseWmi.Checked || checkBoxUseNetLogon.Checked)
            {
                using var token = _token.DuplicateToken(TokenType.Impersonation, SecurityImpersonationLevel.Impersonation, TokenAccessRights.MaximumAllowed);
                token.SetDefaultDacl(new Acl(IntPtr.Zero, false));
                using var imp = token.Impersonate();
                if (checkBoxUseWmi.Checked)
                {
                    using var managementClass = new ManagementClass(@"\\.\root\cimv2",
                                                    "Win32_Process",
                                                     new ObjectGetOptions());
                    var inputParams = managementClass.GetMethodParameters("Create");

                    inputParams["CommandLine"] = txtCommandLine.Text;
                    var outParams = managementClass.InvokeMethod("Create",
                                                                 inputParams,
                                                                 new InvokeMethodOptions());
                }
                else
                {
                    var config = new Win32ProcessConfig
                    {
                        CommandLine = txtCommandLine.Text,
                        Desktop = @"WinSta0\Default",
                        Credentials = new UserCredentials("abc", "abc", "abc"),
                        LogonFlags = CreateProcessLogonFlags.NetCredentialsOnly | CreateProcessLogonFlags.WithProfile
                    };

                    using var process = config.Create();
                }
            }
            else
            {
                using (CreateProcessForToken(txtCommandLine.Text, _token, checkBoxMakeInteractive.Checked))
                {
                }
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }            
    }

    private void btnComputeSafer_Click(object sender, EventArgs e)
    {
        try
        {
            OpenForm(Win32Security.GetTokenFromSaferLevel(_token,
                (SaferLevelId)comboBoxSaferLevel.SelectedItem, checkBoxSaferMakeInert.Checked), "Safer", false);                  
        }
        catch (Exception ex)
        {
            MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private static bool ParseNum(string str, out uint num)
    {
        num = 0;
        if (string.IsNullOrWhiteSpace(str))
        {
            return false;
        }

        if (str.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
        {
            return uint.TryParse(str.Substring(2), NumberStyles.HexNumber, null, out num);
        }

        return uint.TryParse(str, out num);            
    }

    private static TokenIntegrityLevel GetILFromComboBox(ComboBox comboBox)
    {
        uint il_num;
        if (ParseNum(comboBox.Text, out il_num))
            return (TokenIntegrityLevel)il_num;

        if (comboBox.SelectedItem == null)
        {
            return TokenIntegrityLevel.Medium;
        }
        
        return (TokenIntegrityLevel)comboBox.SelectedItem;
    }

    private void comboBoxIL_SelectedIndexChanged(object sender, EventArgs e)
    {
        TokenIntegrityLevel il = GetILFromComboBox(comboBoxIL);

        if (_token.IntegrityLevel!= il)
        {
            btnSetIL.Enabled = true;
        }
        else
        {
            btnSetIL.Enabled = false;
        }
    }

    private void btnSetIL_Click(object sender, EventArgs e)
    {
        TokenIntegrityLevel il = GetILFromComboBox(comboBoxIL);
        if (_token.IntegrityLevel!= il)
        {
            try
            {
                _token.SetIntegrityLevel(il);
                btnSetIL.Enabled = false;
                UpdatePrivileges();
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }

    private void btnBrowse_Click(object sender, EventArgs e)
    {
        using SaveFileDialog dlg = new();
        dlg.FileName = txtFilePath.Text;
        dlg.Filter = "All Files (*.*)|*.*";

        if (dlg.ShowDialog(this) == DialogResult.OK)
        {
            txtFilePath.Text = dlg.FileName;
        }
    }

    private void btnCreate_Click(object sender, EventArgs e)
    {
        try
        {
            using NtToken token = _token.DuplicateToken(TokenType.Impersonation,
                SecurityImpersonationLevel.Impersonation, TokenAccessRights.MaximumAllowed);
            using (var imp = token.Impersonate())
            {
                File.WriteAllText(txtFilePath.Text, txtFileContents.Text);
            }

            MessageBox.Show(this, "Success", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        catch (Exception ex)
        {
            MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    static bool AllPrivsEnabled(IEnumerable<TokenPrivilege> privs)
    {            
        foreach (TokenPrivilege priv in privs)
        {
            if (!priv.Enabled)
            {
                return false;
            }
        }
        return true;
    }

    private void contextMenuStripPrivileges_Opening(object sender, CancelEventArgs e)
    {
        if (listViewPrivs.SelectedItems.Count > 0)
        {
            if (AllPrivsEnabled(GetSelectedItemTags(listViewPrivs).OfType<TokenPrivilege>()))
            {
                enablePrivilegeToolStripMenuItem.Text = "Disable Privilege";
            }
            else
            {
                enablePrivilegeToolStripMenuItem.Text = "Enable Privilege";
            }
            enablePrivilegeToolStripMenuItem.Enabled = true;
        }
        else
        {
            enablePrivilegeToolStripMenuItem.Enabled = false;
        }
    }

    private void ModifyPrivileges(IEnumerable<TokenPrivilege> privs, PrivilegeAttributes attributes)
    {
        bool multi = privs.Count() > 1;
        foreach (TokenPrivilege priv in privs)
        {
            try
            {
                _token.SetPrivilege(priv.Luid, attributes);
            }
            catch (Exception ex)
            {
                if (!multi)
                {
                    MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
        UpdatePrivileges();
    }

    private void enablePrivilegeToolStripMenuItem_Click(object sender, EventArgs e)
    {
        if (listViewPrivs.SelectedItems.Count > 0)
        {
            IEnumerable<TokenPrivilege> privs = GetSelectedItemTags(listViewPrivs).OfType<TokenPrivilege>();
            bool all_enabled = AllPrivsEnabled(privs);
            PrivilegeAttributes attributes = all_enabled ? PrivilegeAttributes.Disabled : PrivilegeAttributes.Enabled;
            ModifyPrivileges(privs, attributes);
        }
    }

    private static void SelectAllItems(ListView listView)
    {
        foreach (ListViewItem item in listView.Items)
        {
            item.Selected = true;
        }
    }

    private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
    {
        SelectAllItems(listViewPrivs);
    }

    private void selectAllGroupsToolStripMenuItem_Click(object sender, EventArgs e)
    {
        SelectAllItems(listViewGroups);
    }

    List<UserGroup> GetSelectedModifiableGroups()
    {
        List<UserGroup> groups = new();
        foreach (UserGroup group in
                listViewGroups.SelectedItems.OfType<ListViewItem>().Select(i => i.Tag))
        {
            if (group != null && !group.Mandatory && !group.DenyOnly)
            {
                groups.Add(group);
            }
        }
        return groups;
    }

    static bool AllGroupsEnabled(IEnumerable<UserGroup> groups)
    {
        foreach (UserGroup group in groups)
        {
            if (!group.Enabled)
            {
                return false;
            }
        }
        return true;
    }

    private void enableGroupToolStripMenuItem_Click(object sender, EventArgs e)
    {
        List<UserGroup> groups = GetSelectedModifiableGroups();
        if (groups.Count > 0)
        {
            bool multi_enable = groups.Count > 1;
            bool all_enabled = AllGroupsEnabled(groups);

            foreach (UserGroup group in groups)
            {
                try
                {
                    if (group != null)
                    {
                        _token.SetGroup(group.Sid, all_enabled ? GroupAttributes.Enabled : GroupAttributes.None);
                    }
                }
                catch (NtException ex)
                {
                    if (!multi_enable)
                    {
                        MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
            UpdateGroupList();
        }
        else
        {
            MessageBox.Show(this, "No Modifable Groups Selected", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void contextMenuStripGroups_Opening(object sender, CancelEventArgs e)
    {
        List<UserGroup> groups = GetSelectedModifiableGroups();
        if (groups.Count > 0)
        {
            if (AllGroupsEnabled(groups))
            {
                enableGroupToolStripMenuItem.Text = "Disable Group";
            }
            else
            {
                enableGroupToolStripMenuItem.Text = "Enable Group";
            }
            enableGroupToolStripMenuItem.Enabled = true;
        }
        else
        {
            enableGroupToolStripMenuItem.Enabled = false;
        }
    }

    private void btnCreateSandbox_Click(object sender, EventArgs e)
    {
        using CreateSandboxTokenForm form = new(_token);
        if (form.ShowDialog(this) == DialogResult.OK)
        {
            OpenForm(form.Token, "Sandbox", false);
        }
    }

    private void btnImpersonate_Click(object sender, EventArgs e)
    {
        SecurityImpersonationLevel implevel = SecurityImpersonationLevel.Impersonation;
        try
        {
            if (_token.TokenType== TokenType.Impersonation)
            {
                implevel = _token.ImpersonationLevel;       
            }

            using NtToken token = _token.DuplicateToken(TokenType.Impersonation, implevel, TokenAccessRights.MaximumAllowed);
            TokenIntegrityLevel il = GetILFromComboBox(comboBoxILForDup);
            if (il != token.IntegrityLevel)
            {
                token.SetIntegrityLevel(il);
            }

            NtToken? imptoken = null;
            using (var imp = token.Impersonate())
            {
                imptoken = NtThread.Current.OpenToken();
            }
            if (imptoken != null)
            {
                OpenForm(imptoken, "Impersonation", false);
            }
            else
            {
                MessageBox.Show(this, "Couldn't open thread token", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(this, ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void comboBoxIL_TextUpdate(object sender, EventArgs e)
    {
        comboBoxIL_SelectedIndexChanged(sender, e);
    }

    private IEnumerable<object> GetSelectedItemTags(ListView listView)
    {
        return listView.SelectedItems.OfType<ListViewItem>().Select(i => i.Tag);
    }

    private void removePrivilegeToolStripMenuItem_Click(object sender, EventArgs e)
    {
        ModifyPrivileges(GetSelectedItemTags(listViewPrivs).OfType<TokenPrivilege>(), 
            PrivilegeAttributes.Removed);
    }

    private void btnToggleUIAccess_Click(object sender, EventArgs e)
    {
        try
        {
            _token.SetUIAccess(!_token.UIAccess);
            txtUIAccess.Text = _token.UIAccess.ToString();
            UpdateTokenFlags();
        }
        catch (NtException ex)
        {
            MessageBox.Show(this, ex.Message, 
                "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void btnToggleVirtualizationEnabled_Click(object sender, EventArgs e)
    {
        try
        {
            _token.SetVirtualizationEnabled(!_token.VirtualizationEnabled);
            txtVirtualizationEnabled.Text = _token.VirtualizationEnabled.ToString();
            UpdateTokenFlags();
        }
        catch (NtException ex)
        {
            MessageBox.Show(this, ex.Message,
                "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void listView_ColumnClick(object sender, ColumnClickEventArgs e)
    {
        ListItemComparer.UpdateListComparer(sender as ListView, e.Column);
    }

    private static void CopyToClipboard(StringBuilder builder)
    {
        CopyToClipboard(builder.ToString());
    }

    private static void CopyToClipboard(string builder)
    {
        try
        {
            Clipboard.SetText(builder.ToString());
        }
        catch (ExternalException)
        {
        }
    }

    private static void CopyListViewItems(ListView list, Func<ListViewItem, string?> formatter)
    {
        if (list.SelectedItems.Count > 0)
        {
            StringBuilder builder = new();
            foreach (ListViewItem item in list.SelectedItems)
            {
                string? format = formatter(item);
                if (format != null)
                {
                    builder.AppendLine(format);
                }
            }

            CopyToClipboard(builder);
        }
    }

    private static Control? GetParentControl(object sender)
    {
        if (sender is ToolStripMenuItem item)
        {
            if (item.Owner is ContextMenuStrip menu)
            {
                return menu.SourceControl;
            }
        }
        return null;
    }

    private void CopyListViewItems(object sender, EventArgs e)
    {
        if (GetParentControl(sender) is ListView list)
        {
            CopyListViewItems(list, item => string.Join(" - ", item.SubItems.Cast<ListViewItem.ListViewSubItem>().Select(i => i.Text)));
        }
    }
    
    private void CopySidListViewItems(object sender, EventArgs e)
    {
        if (GetParentControl(sender) is ListView list)
        {
            CopyListViewItems(list, item =>
            {
                if (item.Tag is UserGroup group) {
                    return group.Sid.ToString();
                }
                return null;
            });
        }
    }

    private void SelectAllListViewItems(object sender, EventArgs e)
    {
        if (GetParentControl(sender) is ListView list)
        {
            SelectAllItems(list);
        }
    }

    private void copyAsSDDLToolStripMenuItem_Click(object sender, EventArgs e)
    {
        SecurityDescriptor sd = new();
        sd.Owner = new SecurityDescriptorSid(_token.Owner, true);
        sd.Group = new SecurityDescriptorSid(_token.PrimaryGroup, true);
        sd.Dacl = _token.DefaultDacl;
        CopyToClipboard(sd.ToSddl());
    }

    private void selectAllDaclToolStripMenuItem_Click(object sender, EventArgs e)
    {
        SelectAllItems(listViewDefDacl);
    }
}
